#include "../liolib/lioflat.h"

#define ACTION_IDLE 0
#define ACTION_APPLY 1
#define ACTION_DFT 2
#define ACTION_IDFT 3


const double fmax=100,fs=2*fmax,T=1;
int N=2*round(fmax*T),action=0;
double t,f,signal,*signal_data,dft,dftRe,dftIm,*dft_Im,*dft_Re,*dft_mod,idft,*idft_data,dt=INV(fs),df=INV(T),worktime;

#define S(f,p) (sin(2*PI*f*t+p))
#define C(f,p) (cos(2*PI*f*t+p))
#define P(p) (0.5*S(10.4,p)+C(20.1,p))
double func(double t)
{
	return P(0)-P(PI/4);
}

class DFT:public FlatApplication
{
	Font fontSmall;
	FlatGraph fgSignal,fgDFT,fgIDFT;
	FlatButton bApply,bExit;
	PrecisionTimer pt;

	BOOL create();
	void draw2D();
	BOOL glCreate();
	void glDestroy();
	void update();
	void destroy();
	void reshape();
	BOOL listen(WORD id, WORD code, const void *info);
	
} objDft;

BOOL DFT::listen(WORD id, WORD code, const void *info)
{
	if(id==bApply.id)
	{
		action=ACTION_APPLY;
		return TRUE;
	}
	else if(id==bExit.id)
	{
		terminate();
		return TRUE;
	}
	return FALSE;
}
void DFT::update()
{
	double s,c,tmp;
	int n,k,j;
	switch(action)
	{
		case ACTION_APPLY:
		{
			t=0;
			fgSignal.graph.clear();
			pt.restart();
			for(n=0;n<N;n++)
			{
				signal_data[n]=signal=func(t);
				fgSignal.graph.addValues();
				t+=dt;
			}
			fgSignal.graph.update();
			worktime=pt.time();
			action=ACTION_DFT;
		}
		break;
		case ACTION_DFT:
		{
			f=0;
			fgDFT.graph.clear();
			pt.restart();
			c=signal_data[0];
			for(k=1;k<N;k++)
				c+= signal_data[k];
			dft_Re[0]=dftRe=dft_mod[0]=dft=c/N;
			dft_Im[0]=dftIm=0;
			fgDFT.graph.addValues();
			f+=df;
			for(n=1,j=N-1;n<=j;n++,j--)
			{
				tmp=(2*PI*n)/N;
				c=signal_data[0];
				s=0;
				for(k=1;k<N;k++)
				{
					c+=signal_data[k]*cos(tmp*k);
					s-=signal_data[k]*sin(tmp*k);
				}
				dft_Re[j]=(dft_Re[n]=dftRe=c/N);
				dft_Im[j]=-(dft_Im[n]=dftIm=s/N);
				dft_mod[j]=(dft_mod[n]=dft=sqrt(c*c+s*s)/N);
				fgDFT.graph.addValues();
				f+=df;
			}
			fgDFT.graph.update();
			worktime=pt.time();
			action=ACTION_IDFT;

		}
		break;
		case ACTION_IDFT:
		{
			t=0;
			fgIDFT.graph.clear();
			pt.restart();
			c=dft_Re[0];
			for(n=1;n<N;n++)
				c+= dft_Re[n];
			idft_data[0]=idft=c;
			fgIDFT.graph.addValues();
			t+=dt;
			for(k=1;k<N;k++)
			{
				tmp=(2*PI*k)/N;
				c=dft_Re[0];
				for(n=1;n<N;n++)
					c+= dft_Re[n]*cos(tmp*n)-dft_Im[n]*sin(tmp*n);
				idft_data[k]=idft=c;
				fgIDFT.graph.addValues();
				t+=dt;
			}
			fgIDFT.graph.update();
			worktime=pt.time();
			action=ACTION_IDLE;
		}
		break;
		default:
			return;
	}
	setUpdated(TRUE);
}
void DFT::reshape()
{
	FlatApplication::reshape();
	fgSignal.setBounds(10,100,float(window.width-30)/2,float(window.height-130)/2);
	fgDFT.size.set(float(window.width-20),float(window.height-130)/2);
	fgIDFT.size.set(float(window.width-30)/2,float(window.height-130)/2);
	container.build();
}
BOOL DFT::create ()	
{
	FlatApplication::create();
	name=_T("Discrete Fourier Transform Observer");

	display.width=1024;
	display.height=768;

	format.precision=3;

	fontSmall.setFace(_T("Courier New"));
	fontSmall.height=-12;

	fgSignal.bgColor=INT_GRAY;
	fgSignal.graph.grid.x=3;
	fgSignal.graph.grid.y=5;
	fgSignal.graph.margin=10;
	fgSignal.graph.marginChannel.set(15,60);
	fgSignal.graph.skip=FALSE;
	fgSignal.graph.font=&fontSmall;
	fgSignal.graph.formatX.precision=2;
	fgSignal.graph.formatY.precision=3;
	fgSignal.margin.set(5,10);
	fgSignal.font=&fontSmall;
	fgSignal.fontColor=INT_BLACK;
	fgSignal.graph.smooth=TRUE;

	fgDFT.copy(fgSignal);
	fgDFT.stick(DIR_DOWN,0,10,&fgSignal);
	fgDFT.graph.skip=FALSE;
	fgDFT.graph.grid.x=9;

	fgIDFT.copy(fgSignal);
	fgIDFT.stick(DIR_RIGHT,10,0,&fgSignal);

	bApply.text=_T("Apply");
	bApply.symbol='a';
	bApply.borderLine=1;
	bApply.borderColor=INT_GRAY;
	bApply.activeLine=2;
	bApply.font=&font;
	bApply.fontColor=INT_BLACK;
	bApply.margin.set(20,3);
	bApply.bgColor=0xFFDDDDDD;
	bApply.overColor=INT_WHITE;
	bApply.pressColor=INT_YELLOW;
	bApply.location.set(10,50);
	bApply.adjustSize=TRUE;

	bExit.text=_T("Exit");
	bExit.copy(bApply);
	bExit.stick(DIR_RIGHT,10,0);


	fgSignal.graph.addChannel( _T("time"),NULL,INT_WHITE,N,&t);
	fgSignal.graph.addChannel( _T("signal"),_T("time"),INT_BLUE,N,&signal);

	fgDFT.graph.addChannel( _T("freq"),NULL,INT_WHITE,N,&f);
	fgDFT.graph.addChannel( _T("DFT"),_T("freq"),INT_RED,N,&dft,2);
	fgDFT.graph.addChannel( _T("DFT Re"),_T("freq"),INT_DGREEN,N,&dftRe,1,TRUE);
	fgDFT.graph.addChannel( _T("DFT Im"),_T("freq"),INT_DBLUE,N,&dftIm,1,TRUE);

	fgIDFT.graph.addChannel( _T("time"),NULL,INT_WHITE,N,&t);
	fgIDFT.graph.addChannel( _T("IDFT"),_T("time"),INT_DGREEN,N,&idft);

	add(&fgSignal);
	add(&fgDFT);
	add(&fgIDFT);

	add(&bApply);
	add(&bExit);

	signal_data=new double[N];
	dft_Im=new double[N];
	dft_Re=new double[N];
	dft_mod=new double[N];
	idft_data=new double[N];

	return TRUE;
}
BOOL DFT::glCreate ()
{
	fontSmall.create(window.hDC);
	FlatApplication::glCreate();
	return TRUE;												
}
void DFT::glDestroy()
{
	FlatApplication::glDestroy();
	fontSmall.destroy();
}
void DFT::destroy()
{
	FlatApplication::destroy();
	delete [] signal_data;
	delete [] dft_Im;
	delete [] dft_Re;
	delete [] dft_mod;
	delete [] idft_data;
}
void DFT::draw2D()
{
	font.color=INT_WHITE;
	font.x=10;
	font.y=20;
	glPrint(&font,name);
	font.y+=18;
	glPrint(&font,_T("action=%d;"),action);
	font.x+=100;
	glPrint(&font,_T("count=%d;"),N);
	font.x+=100;
	glPrint(&font,_T("dt=%f s;"),dt);
	font.x+=150;
	glPrint(&font,_T("df=%f Hz;"),df);
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	return objDft.main(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
}